DLO-JZ Optimisation de l'apprentissage - Jour 1¶
Optimisation système d'une boucle d'apprentissage Resnet-152.

Objet du notebook¶
Le but de ce notebook est d'optimiser un code d'apprentissage d'un modèle Resnet-50 sur Imagenet pour Jean Zay en implémentant :
- TP 1 : l'accélération GPU
- TP 2 : l'Automatic Mixed Precision
- TP 3 : le Channels Last Memory Format
- TP 4 : le Profiler
Les cellules dans ce notebook ne sont pas prévues pour être modifiées, sauf rares exceptions indiquées dans les commentaires. Les TP se feront en modifiant les codes dlojz1_X.py.
Les directives de modification seront marquées par l'étiquette TODO dans le notebook suivant.
Les solutions sont présentes dans le répertoire solutions/.
Notebook rédigé par l'équipe assistance IA de l'IDRIS, juin 2023
Environnement de calcul¶
Les fonctions python de gestion de queue SLURM dévelopées par l'IDRIS et les fonctions dédiées à la formation DLO-JZ sont à importer.
Le module d'environnement pour les jobs et la taille des images sont fixés pour ce notebook.
TODO : choisir un pseudonyme (maximum 5 caractères) pour vous différencier dans la queue SLURM et dans les outils collaboratifs pendant la formation et la compétition.
from idr_pytools import display_slurm_queue, gpu_jobs_submitter, search_log
from dlojz_tools import controle_technique, compare, GPU_underthehood, plot_accuracy, lrfind_plot, imagenet_starter
MODULE = 'pytorch-gpu/py3/2.3.0'
image_size = 224
account = 'for@a100'
name = 'pseudo' ## Pseudonyme à choisir
Création d'un répertoire checkpoints/ si cela n'a pas déjà été fait.
!mkdir -p checkpoints
Gestion de la queue SLURM¶
Pour afficher vos jobs dans la queue SLURM :
display_slurm_queue(name)
Done!
Remarque: Cette fonction est utilisée plusieurs fois dans ce notebook. Elle permet d'afficher la queue de manière dynamique, rafraichie toutes les 5 secondes. Elle ne s'arrête que lorsque la queue est vide. Si vous désirez reprendre la main sur le notebook, il vous suffira d'arrêter manuellement la cellule avec le bouton stop. Cela a bien sûr aucun impact les jobs soumis.
Si vous voulez retirer TOUS vos jobs de la queue SLURM, décommenter et exécuter la cellule suivante :
#!scancel -u $USER
Si vous voulez retirer UN de vos jobs de la queue SLURM, décommenter, compléter et exécuter la cellule suivante :
#!scancel <jobid>
Différence entre deux scripts¶
Pour comparer son code avec les solutions mises à disposition, la fonction suivante permet d'afficher une page HTML contenant un différentiel de fichiers texte.
s1 = "./dlojz1_4.py"
s2 = "./solutions/dlojz1_4.py"
compare(s1, s2)
Voir le résultat du différentiel de fichiers sur la page suivante (attention au spoil !) :
import os
import torchvision
import torchvision.transforms as transforms
import torch
import numpy as np
import matplotlib.pyplot as plt
transform = transforms.Compose([
transforms.RandomResizedCrop(224), # Random resize - Data Augmentation
transforms.RandomHorizontalFlip(), # Horizontal Flip - Data Augmentation
transforms.ToTensor(), # convert the PIL Image to a tensor
transforms.Normalize(mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225))
])
train_dataset = torchvision.datasets.ImageNet(root=os.environ['ALL_CCFRSCRATCH']+'/imagenet',
transform=transform)
train_dataset
Dataset ImageNet
Number of datapoints: 1281167
Root location: /lustre/fsn1/projects/idris/for/commun/imagenet
Split: train
StandardTransform
Transform: Compose(
RandomResizedCrop(size=(224, 224), scale=(0.08, 1.0), ratio=(0.75, 1.3333), interpolation=bilinear, antialias=True)
RandomHorizontalFlip(p=0.5)
ToTensor()
Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=4,
shuffle=True)
batch = next(iter(train_loader))
print('X train batch, shape: {}, data type: {}, Memory usage: {} bytes'
.format(batch[0].shape, batch[0].dtype, batch[0].element_size()*batch[0].nelement()))
print('Y train batch, shape: {}, data type: {}, Memory usage: {} bytes'
.format(batch[1].shape, batch[1].dtype, batch[1].element_size()*batch[1].nelement()))
img = batch[0][0].numpy().transpose((1,2,0))
plt.imshow(img)
plt.axis('off')
labels_cls, labels_id = torch.load(os.environ['ALL_CCFRSCRATCH']+'/imagenet/meta.bin')
label = labels_cls[np.unique(labels_id)[batch[1][0].numpy()]]
_ = plt.title('label class: {}'.format(label[0]))
X train batch, shape: torch.Size([4, 3, 224, 224]), data type: torch.float32, Memory usage: 2408448 bytes Y train batch, shape: torch.Size([4]), data type: torch.int64, Memory usage: 32 bytes
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [-2.117904..2.3262744].
Validation set¶
val_transform = transforms.Compose([
transforms.Resize((256, 256)),
transforms.CenterCrop(224),
transforms.ToTensor(), # convert the PIL Image to a tensor
transforms.Normalize(mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225))])
val_dataset = torchvision.datasets.ImageNet(root=os.environ['ALL_CCFRSCRATCH']+'/imagenet', split='val',
transform=val_transform)
val_dataset
Dataset ImageNet
Number of datapoints: 50000
Root location: /lustre/fsn1/projects/idris/for/commun/imagenet
Split: val
StandardTransform
Transform: Compose(
Resize(size=(256, 256), interpolation=bilinear, max_size=None, antialias=True)
CenterCrop(size=(224, 224))
ToTensor()
Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
)
Resnet-152¶
import torchvision.models as models
model = models.resnet152()
print('number of total parameters: {}'.format(sum([p.numel() for p in model.parameters()])))
print('number of trainable parameters: {}'.format(sum([p.numel() for p in model.parameters() if p.requires_grad])))
number of total parameters: 60192808 number of trainable parameters: 60192808
TP1_0 : Baseline CPU¶
Ce TP consiste à appliquer le code baseline pour prendre en main les fonctionnalités de test et découvrir le code.
TODO :
- Exécuter les cellules suivantes (le job prend plus de 10 minutes environ)
- Puis, ouvrir le fichier dlojz1_0.py
Remarque :
- l'option test lance un apprentissage de 50 itérations.
- les chronomètres mesurent les temps de la 2e à la 50e itération et restitue un temps moyen par itération.
- les parties
DON'T MODIFYdans le script ne doivent pas être modifiées.
n_gpu = 1
batch_size = 128
command = f'./dlojz1_0.py -b {batch_size} --image-size {image_size} --test --no-pin-memory --test-nsteps 10'
command
'./dlojz1_0.py -b 128 --image-size 224 --test --no-pin-memory --test-nsteps 10'
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:20:00')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249368 jobid = ['249368']
Copier-coller la sortie jobid = ['xxxxx'] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobid = ['159506']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249368 gpu_p5 pseudo cfor032 R 9:27 1 jean-zay-iam10
Done!
Quizz¶
L'éxécution étant assez longue (~ 10 min.), un quizz vous attend : Quizz TP1_0
controle_technique(jobid)
Train throughput: 5.45 images/second GPU throughput: 5.46 images/second epoch time: 234907.75 seconds ----------- training step time average (fwd/bkwd on GPU): 23.447189 sec (35.1%/64.9%) +/- 0.164168 loading step time average (IO + CPU to GPU transfer): 0.020119 sec +/- 0.008249
Le code baseline dlojz1_0.py a été exécuté sur le CPU (contrairement à ce qui est indiqué par le contrôle technique) en mode test, soit sur 50 itérations.
Dans le prochain exercice nous verrons ensemble l'accélération sur 1 GPU.

TP1_1 : Accélération GPU¶
Voir la documentation pytorch
TODO : dans le script dlojz1_1.py:
Définir la variable
gpuet envoyer le modèle dans la mémoire du GPU.Envoyer les batches d'images d'entrée et les labels associés sur le GPU, pour **les étapes de training et de *validation***.
n_gpu = 1
batch_size = 128
command = f'./dlojz1_1.py -b {batch_size} --image-size {image_size} --test'
command
'./dlojz1_1.py -b 128 --image-size 224 --test'
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
command = f'./dlojz1_1.py -b {batch_size} --image-size {image_size} --test'
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249432 jobid = ['249432']
Copier-coller la sortie jobid = ['xxxxx'] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobid = ['159564']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249432 gpu_p5 pseudo cfor032 R 0:35 1 jean-zay-iam10
Done!
Quizz¶
L'éxécution étant assez longue, un quizz vous attend : Quizz TP1_1
controle_technique(jobid)
Train throughput: 468.86 images/second GPU throughput: 474.73 images/second epoch time: 2732.77 seconds ----------- training step time average (fwd/bkwd on GPU): 0.269628 sec (8.2%/91.8%) +/- 0.000315 loading step time average (IO + CPU to GPU transfer): 0.003376 sec +/- 0.000060
Test d'occupation mémoire¶
Afin de mesurer l'impact de la taille de batch sur l'occupation mémoire et sur le throughput, la cellule suivante permet de soumettre plusieurs jobs avec des tailles de batch croissantes. Dans les cas où la mémoire est saturée et dépasse la capacité du GPU, le système renverra une erreur CUDA Out of Memory.
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
n_gpu = 1
batch_size = [8, 16, 32, 64, 128, 256, 512]
command = [f'./dlojz1_1.py -b {b} --image-size {image_size} --test'
for b in batch_size]
jobids = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobids = {jobids}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249436 Submitted batch job 249437 Submitted batch job 249438 Submitted batch job 249439 Submitted batch job 249440 Submitted batch job 249442 Submitted batch job 249443 jobids = ['249436', '249437', '249438', '249439', '249440', '249442', '249443']
Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobids = ['902357', '902358', '902359', '902360', '902361', '902362', '902363', '902365']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249443 gpu_p5 pseudo cfor032 R 1:01 1 jean-zay-iam07
Done!
GPU_underthehood(jobids)
Batch size per GPU: 8 Max GPU Memory Allocated: 3.34 GB, Troughput: 180.269 images/second Batch size per GPU: 16 Max GPU Memory Allocated: 3.35 GB, Troughput: 307.748 images/second Batch size per GPU: 32 Max GPU Memory Allocated: 5.82 GB, Troughput: 420.239 images/second Batch size per GPU: 64 Max GPU Memory Allocated: 11.19 GB, Troughput: 452.256 images/second Batch size per GPU: 128 Max GPU Memory Allocated: 21.75 GB, Troughput: 473.613 images/second Batch size per GPU: 256 Max GPU Memory Allocated: 43.03 GB, Troughput: 487.266 images/second Batch size per GPU: 512 CUDA out of memory Memory occupancy by Model part : 0.606 +/- 0.172 GB
Le dernier job a atteint le seuil CUDA Out Of Memory :
controle_technique([jobids[-1]])

TP1_2 : Automatic Mixed Precision¶
Voir la documentation de l'IDRIS
TODO : dans le script dlojz1_2.py:
Importer les fonctionnalités liées à l'Automatic Mixed Precision.
Initialiser le scaler.
Implémenter l'autocasting (le changement de précision, FP32 à FP16) dans le forward , avec la ligne
with autocast():dans la boucle de training et la boucle de validation.Implémenter le gradient scaling pour la seule boucle de training. Note: À la place des lignes
loss.backward()etoptimizer.step().
n_gpu = 1
batch_size = 128
command = f'./dlojz1_2.py -b {batch_size} --image-size {image_size} --test'
command
'./dlojz1_2.py -b 128 --image-size 224 --test'
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
command = f'./dlojz1_2.py -b {batch_size} --image-size {image_size} --test'
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249456 jobid = ['249456']
Copier-coller la sortie jobid = ['xxxxx'] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobid = ['902431']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249456 gpu_p5 pseudo cfor032 R 0:37 1 jean-zay-iam12
Done!
Quizz¶
L'éxécution étant assez longue, un quizz vous attend : Quizz TP1_2
controle_technique(jobid)
Train throughput: 736.12 images/second GPU throughput: 751.12 images/second epoch time: 1740.59 seconds ----------- training step time average (fwd/bkwd on GPU): 0.170413 sec (15.9%/84.3%) +/- 0.003061 loading step time average (IO + CPU to GPU transfer): 0.003472 sec +/- 0.000166
Test d'occupation mémoire¶
Afin de mesurer l'impact de la taille de batch sur l'occupation mémoire et sur le throughput, la cellule suivante permet de soumettre plusieurs jobs avec des tailles de batch croissantes. Dans les cas où la mémoire est saturée et dépasse la capacité du GPU, le système renverra une erreur CUDA Out of Memory.
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
n_gpu = 1
batch_size = [16, 32, 64, 128, 256, 512, 1024]
command = [f'./dlojz1_2.py -b {b} --image-size {image_size} --test'
for b in batch_size]
jobids = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobids = {jobids}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249458 Submitted batch job 249476 Submitted batch job 249478 Submitted batch job 249479 Submitted batch job 249480 Submitted batch job 249481 Submitted batch job 249482 jobids = ['249458', '249476', '249478', '249479', '249480', '249481', '249482']
Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobids = ['903148', '903150', '903151', '903152', '903154', '903155', '903156', '903157']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249481 gpu_p5 pseudo cfor032 R 1:23 1 jean-zay-iam21
Done!
GPU_underthehood(jobids)
Batch size per GPU: 16 Max GPU Memory Allocated: 2.20 GB, Troughput: 302.401 images/second Batch size per GPU: 32 Max GPU Memory Allocated: 3.36 GB, Troughput: 527.546 images/second Batch size per GPU: 64 Max GPU Memory Allocated: 6.02 GB, Troughput: 678.685 images/second Batch size per GPU: 128 Max GPU Memory Allocated: 11.48 GB, Troughput: 750.371 images/second Batch size per GPU: 256 Max GPU Memory Allocated: 22.20 GB, Troughput: 733.232 images/second Batch size per GPU: 512 Max GPU Memory Allocated: 43.84 GB, Troughput: 753.701 images/second Batch size per GPU: 1024 CUDA out of memory Memory occupancy by Model part : 0.645 +/- 0.084 GB
Changement de taille de batch¶
TODO : Choisir pour la suite du TP une taille de batch par GPU qui vous semble la plus pertinente selon le test précédent.
## Choisir un batch size optimal
bs_optim = 512
n_gpu = 1
command = f'./dlojz1_2.py -b {bs_optim} --image-size {image_size} --test'
command
'./dlojz1_2.py -b 512 --image-size 224 --test'
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
command = f'./dlojz1_2.py -b {bs_optim} --image-size {image_size} --test'
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249542 jobid = ['249542']
Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobid = ['903167']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249542 gpu_p5 pseudo cfor032 CG 1:08 1 jean-zay-iam12
Done!
controle_technique(jobid)
Train throughput: 737.36 images/second GPU throughput: 751.50 images/second epoch time: 1738.00 seconds ----------- training step time average (fwd/bkwd on GPU): 0.681303 sec (7.6%/92.5%) +/- 0.008765 loading step time average (IO + CPU to GPU transfer): 0.013063 sec +/- 0.000205

TP1_3 : Channels Last Memory Format¶
Voir la documentation pytorch
TODO : dans le script dlojz1_3.py:
Lors de l'envoie du modèle au GPU, configurer le paramètre
memory_formatavec l'option Channel Last Memory.Lors de l'envoie des images d'entrée au GPU, configurer le paramètre
memory_formatavec l'option Channel Last Memory.
n_gpu = 1
command = f'./dlojz1_3.py -b {bs_optim} --image-size {image_size} --test'
command
'./dlojz1_3.py -b 512 --image-size 224 --test'
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
command = f'./dlojz1_3.py -b {bs_optim} --image-size {image_size} --test'
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249549 jobid = ['249549']
Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobid = ['902659']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249549 gpu_p5 pseudo cfor032 R 0:50 1 jean-zay-iam12
Done!
controle_technique(jobid)
Train throughput: 1162.16 images/second GPU throughput: 1199.55 images/second epoch time: 1102.72 seconds ----------- training step time average (fwd/bkwd on GPU): 0.426828 sec (6.4%/94.0%) +/- 0.012713 loading step time average (IO + CPU to GPU transfer): 0.013729 sec +/- 0.000413
Test d'occupation mémoire¶
Afin de mesurer l'impact de la taille de batch sur l'occupation mémoire et sur le throughput, la cellule suivante permet de soumettre plusieurs jobs avec des tailles de batch croissantes. Dans les cas où la mémoire est saturée et dépasse la capacité du GPU, le système renverra une erreur CUDA Out of Memory.
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
n_gpu = 1
batch_size = [16, 32, 64, 128, 256, 512, 1024]
command = [f'./dlojz1_3.py -b {b} --image-size {image_size} --test'
for b in batch_size]
jobids = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobids = {jobids}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249557 Submitted batch job 249558 Submitted batch job 249559 Submitted batch job 249561 Submitted batch job 249562 Submitted batch job 249563 Submitted batch job 249564 jobids = ['249557', '249558', '249559', '249561', '249562', '249563', '249564']
Copier-coller la sortie jobids = ['xxxxx', ...] dans la cellule suivante.
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobids = ['2069429', '2069430', '2069431', '2069432', '2069433', '2069435', '2069437']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249564 gpu_p5 pseudo cfor032 CG 1:10 1 jean-zay-iam21
Done!
GPU_underthehood(jobids)
Batch size per GPU: 16 Max GPU Memory Allocated: 2.10 GB, Troughput: 307.175 images/second Batch size per GPU: 32 Max GPU Memory Allocated: 3.31 GB, Troughput: 570.445 images/second Batch size per GPU: 64 Max GPU Memory Allocated: 6.01 GB, Troughput: 966.245 images/second Batch size per GPU: 128 Max GPU Memory Allocated: 11.48 GB, Troughput: 1110.966 images/second Batch size per GPU: 256 Max GPU Memory Allocated: 22.22 GB, Troughput: 1158.450 images/second Batch size per GPU: 512 Max GPU Memory Allocated: 43.88 GB, Troughput: 1196.800 images/second Batch size per GPU: 1024 CUDA out of memory Memory occupancy by Model part : 0.612 +/- 0.077 GB

Garage - Mise à niveau¶
On fixe le batch size et la taille d'image pour ce TP.
image_size = 224
TP1_4 : Profiler¶
Implémentation du profiler PyTorch¶
Voir la documentation de l'IDRIS.
TODO : dans le script dlojz1_4.py :
Importer les librairies liées au profiler PyTorch.
Configurer le profiler et ses paramètres.
# pytorch profiler setup
prof = profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
schedule=schedule(wait=1, warmup=1, active=5, repeat=1),
on_trace_ready=tensorboard_trace_handler('./profiler/' + os.environ['SLURM_JOB_NAME']
+ '_' + os.environ['SLURM_JOBID'] + '_bs' +
str(mini_batch_size) + '_is' + str(args.image_size)),
profile_memory=True,
record_shapes=False,
with_stack=False,
with_flops=False
)
Englober toute la boucle d'apprentissage (validation comprise) dans le context
prof.Indiquer au profiler la fin de chaque itération d'apprentissage (avant la validation).
Ajouter des balises comme suit :
#TODO tag forward step with record functions
with record_function("optimizer zero grad"): optimizer.zero_grad()
# Implement autocasting
with autocast():
with record_function("inference"): outputs = model(images)
with record_function("loss function"): loss = criterion(outputs, labels)
if args.test: chrono.backward()
#TODO tag backward step with record functions
# Implement gradient scaling
with record_function("gradient compute"): scaler.scale(loss).backward()
with record_function("optimizer step and weights update"): scaler.step(optimizer)
with record_function("Scaler update"): scaler.update()
Génération d'une trace profiler¶
Soumission du job. Attention vous sollicitez les noeuds de calcul à ce moment-là.
Remarques :
- le profilage sera actif sur 5 steps donc nous n'exécutons l'entraînement que sur 8 steps grâce à l'argument
--test-nsteps 8. - Nous lancerons deux jobs de prise de trace, avec une taille de batch de
512puis une taille de batch de64.
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
command = [f'./dlojz1_4.py -b 512 --image-size {image_size} --test --test-nsteps 8',
f'./dlojz1_4.py -b 64 --image-size {image_size} --test --test-nsteps 8']
n_gpu = 1
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249566 Submitted batch job 249567 jobid = ['249566', '249567']
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobid = ['205798', '205799']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249566 gpu_p5 pseudo cfor032 R 0:45 1 jean-zay-iam10
Done!
controle_technique(jobid[0])
Train throughput: 1162.38 images/second GPU throughput: 1164.65 images/second epoch time: 1102.51 seconds ----------- training step time average (fwd/bkwd on GPU): 0.439615 sec (10.1%/91.5%) +/- 0.015320 loading step time average (IO + CPU to GPU transfer): 0.000860 sec +/- 0.001123
TODO : vérifier que 2 traces ont bien été générées dans les répertoires profiler/<name>_<jobid>_bs512_is224/ et profiler/<name>_<jobid>_bs64_is224/ et sous la forme d'un fichier .json:
!tree profiler/
profiler/
├── pseudo_249566_bs512_is224
│ └── jean-zay-iam10_1529868.1729184744185269755.pt.trace.json
└── pseudo_249567_bs64_is224
└── jean-zay-iam12_3791290.1729184734641036112.pt.trace.json
2 directories, 2 files
Visualisation des traces profiler avec TensorBoard ¶
TODO : visualiser cette trace grâce à l'application TensorBoard en suivant les étapes suivantes :
- ouvrir jupyterhub.idris.fr dans un nouvel onglet du navigateur
- ouvrir une nouvelle instance JupyterHub en cliquant sur Add New JupyterLab Instance
- sélectionner Spawn server on SLURM node (on va réserver un GPU)
- sélectionner Tensorboard dans le menu Frontend
- définir le chemin des logs $WORK/DLO-JZ/Jour1/tp_dlojz_jour1/profiler dans TensorBoard logs directory

- sélectionner l'option avancée
--partition=Octo-GPU A100 SXM4 with 80 GB GPU mem

- lancer l'instance TensorBoard

Remarque : le premier démarrage de TensorBoard peut prendre un peu de temps. Il faut parfois faire preuve d'un peu de patience lorsqu'on utilise cet outil mais ça en vaut la peine :)
Vous disposez de 2 traces : une avec un batch_size de 512 et l'autre avec un batch_size de 64.
TODO : en naviguant dans les différents onglets du TensorBoard, chercher à répondre aux questions suivantes :
- Comparer les 2 traces : le GPU est-il bien utilisé ? (mémoire max utilisée, occupancy, efficiency)
- les TensorCores sont-ils bien sollicités ? Quelles sont les couches éligibles aux TensorCores? (Voir les vue Operator et GPU Kernel)
- essayer de repérer les grandes étapes de calcul sur la timeline de l'exécution (onglet Trace)
IMPORTANT : une fois le TP terminé, penser à quitter l'instance JupyterHub pour libérer le GPU ( > Hub Control Panel > Cancel ).
Optionnel : Profiler la validation¶
TODO : dans le script dlojz1_4.py :
- Ajouter des balises comme suit :
# Runs the forward pass with no grad mode.
with torch.no_grad():
# Implement autocasting
with autocast():
with record_function("inference"): val_outputs = model(val_images)
with record_function("loss function"): val_loss = criterion(val_outputs, val_labels)
- Enlever l'indication de fin d'itération d'apprentissage.
- A la place, indiquer au profiler la fin de chaque itération de validation (dans la boucle de validation).
- Reprendre une trace profiler
- La visualiser sur Tensorboard
Pour soumettre le job, veuillez basculer la cellule suivante du mode Raw NBConvert au mode Code.
command = f'./dlojz1_4.py -b 512 --image-size {image_size} --test --test-nsteps 8'
n_gpu = 1
jobid = gpu_jobs_submitter(command, n_gpu, MODULE, name=name,
account=account, time_max='00:10:00')
print(f'jobid = {jobid}')
batch job 0: 1 GPUs distributed on 1 nodes with 1 tasks / 1 gpus per node and 8 cpus per task Submitted batch job 249578 jobid = ['249578']
Puis, rebasculer la cellule précédente en mode Raw NBConvert, afin d'eviter de relancer un job par erreur.
#jobid = ['159708']
display_slurm_queue(name)
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
249578 gpu_p5 pseudo cfor032 R 0:44 1 jean-zay-iam10
Done!
controle_technique(jobid)
Train throughput: 1169.68 images/second GPU throughput: 1170.68 images/second epoch time: 1095.63 seconds ----------- training step time average (fwd/bkwd on GPU): 0.437353 sec (6.4%/94.0%) +/- 0.006370 loading step time average (IO + CPU to GPU transfer): 0.000375 sec +/- 0.000117
TODO : dans le script dlojz1_4.py :
- Vous pouvez ensuite changer la valeur de la variable
VAL_BATCH_SIZE=250 - Reprendre une trace profiler
- La visualiser sur Tensorboard
